﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Web.Security;
using System.Configuration.Provider;
using Db8.Data;
using Db8.DataAccess;

namespace Db8.Providers
{
    public class Db8RoleProvider : RoleProvider
    {

        #region Public RoleProvider Methods

        public override string ApplicationName { get; set; }

        public override void Initialize(string name, NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }
            if (string.IsNullOrEmpty(name))
            {
                name = "Db8RoleProvider";
            }
            if (string.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Role Provider for Db8");
            }

            base.Initialize(name, config);

            ApplicationName = config["applicationName"];
            if (string.IsNullOrEmpty(ApplicationName))
                ApplicationName = "/";
        }


        public override void CreateRole(string roleName)
        {
            using (Db8Context db = new Db8Context())
            {
                if (!db.Roles.Any(r => r.Name == roleName))
                {
                    Role role = new Role()
                    {
                        Name = roleName,
                    };
                    db.Roles.Add(role);
                    db.SaveChanges();
                }
            }
        }

        public override bool RoleExists(string roleName)
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Roles.Any(r => r.Name == roleName);
            }
        }

        public override string[] GetAllRoles()
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Roles.Select(r => r.Name).ToArray();
            }
        }

        public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
        {
            using (Db8Context db = new Db8Context())
            {
                if (throwOnPopulatedRole)
                {
                    if (db.UserRoles.Any(ur => ur.Role.Name == roleName))
                    {
                        throw new ProviderException("Cannot delete a role with users");
                    }
                }

                var role = db.Roles
                    .Where(r => r.Name == roleName)
                    .FirstOrDefault();

                if (role == null)
                    return false;

                db.Roles.Remove(role);
                db.SaveChanges();
                return true;
            }
        }


        public override void AddUsersToRoles(string[] usernames, string[] roleNames)
        {
            using (Db8Context db = new Db8Context())
            {
                var users = db.Users
                    .Include("UserRoles")
                    .Include("UserRoles.User")
                    .Include("UserRoles.Role")
                    .WhereIn(u => u.Username, usernames);
                var roles = db.Roles
                    .WhereIn(r => r.Name, roleNames);
                foreach (var user in users)
                {
                    foreach (var role in roles)
                    {
                        if (!user.UserRoles.Any(ur => ur.RoleId == role.RoleId))
                            user.UserRoles.Add(new UserRole()
                            {
                                User = user,
                                Role = role
                            });
                    }
                }
                db.SaveChanges();
            }
        }

        public override string[] FindUsersInRole(string roleName, string usernameToMatch)
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Users
                    .Where(u => u.UserRoles.Any(ur => ur.Role.Name == roleName))
                    .Where(u => usernameToMatch == null || u.Username.Contains(usernameToMatch))
                    .Select(u => u.Username)
                    .ToArray();
            }
        }


        public override string[] GetRolesForUser(string username)
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Roles
                    .Where(r => r.RoleUsers.Any(ru => ru.User.Username == username))
                    .Select(r => r.Name)
                    .ToArray();
            }
        }

        public override string[] GetUsersInRole(string roleName)
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Users
                    .Where(u => u.UserRoles.Any(ur => ur.Role.Name == roleName))
                    .Select(u => u.Username)
                    .ToArray();
            }
        }

        public override bool IsUserInRole(string username, string roleName)
        {
            using (Db8Context db = new Db8Context())
            {
                return db.Users
                    .Where(u => u.UserRoles.Any(ur => ur.Role.Name == roleName))
                    .Where(u => u.Username == username)
                    .Any();
            }
        }

        public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
        {
            using (Db8Context db = new Db8Context())
            {
                var users = db.Users
                    .Include("UserRoles")
                    .Include("UserRoles.User")
                    .Include("UserRoles.Role")
                    .WhereIn(u => u.Username, usernames);
                var roles = db.Roles
                    .WhereIn(r => r.Name, roleNames);
                foreach (var user in users)
                {
                    foreach (var role in roles)
                    {
                        if (user.UserRoles.Any(ur => ur.RoleId == role.RoleId))
                            user.UserRoles.Remove(user.UserRoles.Where(ur => ur.RoleId == role.RoleId).First());
                    }
                }
                db.SaveChanges();
            }
        }



        #endregion


    }
}
